using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using LiteNetLib.Utils;

namespace LiteNetLib
{
	public sealed class NetManager : INetSocketListener, IConnectionRequestListener, IEnumerable<NetPeer>, IEnumerable
	{
		private class IPEndPointComparer : IEqualityComparer<IPEndPoint>
		{
			public bool Equals(IPEndPoint x, IPEndPoint y)
			{
				return x.Equals(y);
			}

			public int GetHashCode(IPEndPoint obj)
			{
				return obj.GetHashCode();
			}
		}

		[CompilerGenerated]
		private sealed class _003CGetEnumerator_003Ed__104 : IEnumerator<NetPeer>, IDisposable, IEnumerator
		{
			private int _003C_003E1__state;

			private NetPeer _003C_003E2__current;

			public NetManager _003C_003E4__this;

			private NetPeer _003Cpeer_003E5__2;

			NetPeer IEnumerator<NetPeer>.Current
			{
				[DebuggerHidden]
				get
				{
					return _003C_003E2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return _003C_003E2__current;
				}
			}

			[DebuggerHidden]
			public _003CGetEnumerator_003Ed__104(int _003C_003E1__state)
			{
				this._003C_003E1__state = _003C_003E1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
			}

			private bool MoveNext()
			{
				int num = _003C_003E1__state;
				NetManager netManager = _003C_003E4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					_003C_003E1__state = -1;
					_003Cpeer_003E5__2 = netManager._headPeer;
					break;
				case 1:
					_003C_003E1__state = -1;
					_003Cpeer_003E5__2 = _003Cpeer_003E5__2.NextPeer;
					break;
				}
				if (_003Cpeer_003E5__2 != null)
				{
					_003C_003E2__current = _003Cpeer_003E5__2;
					_003C_003E1__state = 1;
					return true;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private readonly NetSocket _socket;

		private Thread _logicThread;

		private readonly Queue<NetEvent> _netEventsQueue;

		private readonly Stack<NetEvent> _netEventsPool;

		private readonly INetEventListener _netEventListener;

		private readonly Dictionary<IPEndPoint, NetPeer> _peersDict;

		private readonly ReaderWriterLockSlim _peersLock;

		private volatile NetPeer _headPeer;

		private volatile int _connectedPeersCount;

		private readonly List<NetPeer> _connectedPeerListCache;

		private int _lastPeerId;

		private readonly Queue<int> _peerIds;

		internal readonly NetPacketPool NetPacketPool;

		public bool UnconnectedMessagesEnabled;

		public bool NatPunchEnabled;

		public int UpdateTime = 15;

		public int PingInterval = 1000;

		public int DisconnectTimeout = 5000;

		public bool SimulatePacketLoss;

		public bool SimulateLatency;

		public int SimulationPacketLossChance = 10;

		public int SimulationMinLatency = 30;

		public int SimulationMaxLatency = 100;

		public bool UnsyncedEvents;

		public bool DiscoveryEnabled;

		public int ReconnectDelay = 500;

		public int MaxConnectAttempts = 10;

		public bool ReuseAddress;

		private const int DefaultUpdateTime = 15;

		public readonly NetStatistics Statistics;

		public readonly NatPunchModule NatPunchModule;

		[CompilerGenerated]
		private bool _003CIsRunning_003Ek__BackingField;

		public bool AutoRecycle;

		public bool IsRunning
		{
			[CompilerGenerated]
			get
			{
				return _003CIsRunning_003Ek__BackingField;
			}
			[CompilerGenerated]
			private set
			{
				_003CIsRunning_003Ek__BackingField = value;
			}
		}

		public int LocalPort
		{
			get
			{
				return _socket.LocalPort;
			}
		}

		public NetPeer FirstPeer
		{
			get
			{
				return _headPeer;
			}
		}

		public List<NetPeer> ConnectedPeerList
		{
			get
			{
				_connectedPeerListCache.Clear();
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					if ((netPeer.ConnectionState & ConnectionState.Connected) != 0)
					{
						_connectedPeerListCache.Add(netPeer);
					}
				}
				return _connectedPeerListCache;
			}
		}

		public int PeersCount
		{
			get
			{
				return _connectedPeersCount;
			}
		}

		private bool TryGetPeer(IPEndPoint endPoint, out NetPeer peer)
		{
			_peersLock.EnterReadLock();
			bool result = _peersDict.TryGetValue(endPoint, out peer);
			_peersLock.ExitReadLock();
			return result;
		}

		private NetPeer TryAddPeer(NetPeer peer)
		{
			_peersLock.EnterUpgradeableReadLock();
			NetPeer value;
			if (_peersDict.TryGetValue(peer.EndPoint, out value))
			{
				lock (_peerIds)
				{
					_peerIds.Enqueue(peer.Id);
				}
				_peersLock.ExitUpgradeableReadLock();
				return value;
			}
			_peersLock.EnterWriteLock();
			if (_headPeer != null)
			{
				peer.NextPeer = _headPeer;
				_headPeer.PrevPeer = peer;
			}
			_headPeer = peer;
			_peersDict.Add(peer.EndPoint, peer);
			_peersLock.ExitWriteLock();
			_peersLock.ExitUpgradeableReadLock();
			return peer;
		}

		private void RemovePeer(NetPeer peer)
		{
			_peersLock.EnterWriteLock();
			RemovePeerInternal(peer);
			_peersLock.ExitWriteLock();
		}

		private void RemovePeerInternal(NetPeer peer)
		{
			if (!_peersDict.Remove(peer.EndPoint))
			{
				return;
			}
			if (peer == _headPeer)
			{
				_headPeer = peer.NextPeer;
			}
			if (peer.PrevPeer != null)
			{
				peer.PrevPeer.NextPeer = peer.NextPeer;
			}
			if (peer.NextPeer != null)
			{
				peer.NextPeer.PrevPeer = peer.PrevPeer;
			}
			peer.PrevPeer = null;
			peer.NextPeer = null;
			lock (_peerIds)
			{
				_peerIds.Enqueue(peer.Id);
			}
		}

		public NetManager(INetEventListener listener)
		{
			_socket = new NetSocket(this);
			_netEventListener = listener;
			_netEventsQueue = new Queue<NetEvent>();
			_netEventsPool = new Stack<NetEvent>();
			NetPacketPool = new NetPacketPool();
			NatPunchModule = new NatPunchModule(_socket);
			Statistics = new NetStatistics();
			_connectedPeerListCache = new List<NetPeer>();
			_peersDict = new Dictionary<IPEndPoint, NetPeer>(new IPEndPointComparer());
			_peersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
			_peerIds = new Queue<int>();
		}

		internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency)
		{
			CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, null, SocketError.Success, latency);
		}

		internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			int result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
			NetPacketPool.Recycle(packet);
			return result;
		}

		internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
		}

		internal int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
		{
			if (!IsRunning)
			{
				return 0;
			}
			SocketError errorCode = SocketError.Success;
			int num = _socket.SendTo(message, start, length, remoteEndPoint, ref errorCode);
			NetPeer peer;
			switch (errorCode)
			{
			case SocketError.MessageSize:
				return -1;
			case SocketError.HostUnreachable:
				if (TryGetPeer(remoteEndPoint, out peer))
				{
					DisconnectPeerForce(peer, DisconnectReason.HostUnreachable, errorCode, null);
				}
				CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, errorCode);
				return -1;
			case SocketError.ConnectionReset:
				if (TryGetPeer(remoteEndPoint, out peer))
				{
					DisconnectPeerForce(peer, DisconnectReason.RemoteConnectionClose, errorCode, null);
				}
				return -1;
			default:
				if (num <= 0)
				{
					return 0;
				}
				return num;
			}
		}

		internal void DisconnectPeerForce(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, NetPacket eventData)
		{
			DisconnectPeer(peer, reason, socketErrorCode, true, null, 0, 0, eventData);
		}

		private void DisconnectPeer(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, bool force, byte[] data, int start, int count, NetPacket eventData)
		{
			bool flag = peer.ConnectionState == ConnectionState.Connected;
			if (peer.Shutdown(data, start, count, force))
			{
				if (flag)
				{
					_connectedPeersCount--;
				}
				CreateEvent(NetEvent.EType.Disconnect, peer, null, socketErrorCode, 0, reason, null, DeliveryMethod.Unreliable, eventData);
			}
		}

		private void CreateEvent(NetEvent.EType type, NetPeer peer = null, IPEndPoint remoteEndPoint = null, SocketError errorCode = SocketError.Success, int latency = 0, DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, ConnectionRequest connectionRequest = null, DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, NetPacket readerSource = null)
		{
			NetEvent netEvent = null;
			if (type == NetEvent.EType.Connect)
			{
				_connectedPeersCount++;
			}
			lock (_netEventsPool)
			{
				if (_netEventsPool.Count > 0)
				{
					netEvent = _netEventsPool.Pop();
				}
			}
			if (netEvent == null)
			{
				netEvent = new NetEvent(this);
			}
			netEvent.Type = type;
			netEvent.DataReader.SetSource(readerSource);
			netEvent.Peer = peer;
			netEvent.RemoteEndPoint = remoteEndPoint;
			netEvent.Latency = latency;
			netEvent.ErrorCode = errorCode;
			netEvent.DisconnectReason = disconnectReason;
			netEvent.ConnectionRequest = connectionRequest;
			netEvent.DeliveryMethod = deliveryMethod;
			if (UnsyncedEvents)
			{
				ProcessEvent(netEvent);
				return;
			}
			lock (_netEventsQueue)
			{
				_netEventsQueue.Enqueue(netEvent);
			}
		}

		private void ProcessEvent(NetEvent evt)
		{
			bool isNull = evt.DataReader.IsNull;
			switch (evt.Type)
			{
			case NetEvent.EType.Connect:
				_netEventListener.OnPeerConnected(evt.Peer);
				break;
			case NetEvent.EType.Disconnect:
			{
				DisconnectInfo disconnectInfo = default(DisconnectInfo);
				disconnectInfo.Reason = evt.DisconnectReason;
				disconnectInfo.AdditionalData = evt.DataReader;
				disconnectInfo.SocketErrorCode = evt.ErrorCode;
				DisconnectInfo disconnectInfo2 = disconnectInfo;
				_netEventListener.OnPeerDisconnected(evt.Peer, disconnectInfo2);
				break;
			}
			case NetEvent.EType.Receive:
				_netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.DeliveryMethod);
				break;
			case NetEvent.EType.ReceiveUnconnected:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage);
				break;
			case NetEvent.EType.DiscoveryRequest:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.DiscoveryRequest);
				break;
			case NetEvent.EType.DiscoveryResponse:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.DiscoveryResponse);
				break;
			case NetEvent.EType.Error:
				_netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode);
				break;
			case NetEvent.EType.ConnectionLatencyUpdated:
				_netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency);
				break;
			case NetEvent.EType.ConnectionRequest:
				_netEventListener.OnConnectionRequest(evt.ConnectionRequest);
				break;
			}
			if (isNull)
			{
				RecycleEvent(evt);
			}
			else if (AutoRecycle)
			{
				evt.DataReader.Recycle();
			}
		}

		internal void RecycleEvent(NetEvent evt)
		{
			evt.Peer = null;
			evt.ErrorCode = SocketError.Success;
			evt.RemoteEndPoint = null;
			evt.ConnectionRequest = null;
			lock (_netEventsPool)
			{
				_netEventsPool.Push(evt);
			}
		}

		private void UpdateLogic()
		{
			List<NetPeer> list = new List<NetPeer>();
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			while (IsRunning)
			{
				int num = (int)stopwatch.ElapsedMilliseconds;
				if (num <= 0)
				{
					num = 1;
				}
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > DisconnectTimeout)
					{
						list.Add(netPeer);
					}
					else
					{
						netPeer.Update(num);
					}
				}
				if (list.Count > 0)
				{
					_peersLock.EnterWriteLock();
					for (int i = 0; i < list.Count; i++)
					{
						RemovePeerInternal(list[i]);
					}
					_peersLock.ExitWriteLock();
					list.Clear();
				}
				int num2 = UpdateTime - (int)(stopwatch.ElapsedMilliseconds - num);
				stopwatch.Reset();
				stopwatch.Start();
				if (num2 > 0)
				{
					Thread.Sleep(num2);
				}
			}
			stopwatch.Stop();
		}

		void INetSocketListener.OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint)
		{
			if (errorCode != 0)
			{
				CreateEvent(NetEvent.EType.Error, null, null, errorCode);
				NetDebug.WriteError("[NM] Receive error: {0}", errorCode);
				return;
			}
			try
			{
				DataReceived(data, length, remoteEndPoint);
			}
			catch (Exception ex)
			{
				NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex);
			}
		}

		void IConnectionRequestListener.OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length)
		{
			if (request.Result == ConnectionRequestResult.Reject)
			{
				request.Peer.Reject(request.ConnectionId, request.ConnectionNumber, rejectData, start, length);
				return;
			}
			request.Peer.Accept(request.ConnectionId, request.ConnectionNumber);
			CreateEvent(NetEvent.EType.Connect, request.Peer);
		}

		private int GetNextPeerId()
		{
			int num;
			lock (_peerIds)
			{
				if (_peerIds.Count == 0)
				{
					num = _lastPeerId++;
					num = num;
				}
				else
				{
					num = _peerIds.Dequeue();
				}
			}
			return num;
		}

		private void ProcessConnectRequest(IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest)
		{
			byte connectionNumber = connRequest.ConnectionNumber;
			if (netPeer != null)
			{
				switch (netPeer.ProcessConnectRequest(connRequest))
				{
				default:
					return;
				case ConnectRequestResult.Reconnection:
					DisconnectPeerForce(netPeer, DisconnectReason.RemoteConnectionClose, SocketError.Success, null);
					RemovePeer(netPeer);
					break;
				case ConnectRequestResult.NewConnection:
					RemovePeer(netPeer);
					break;
				case ConnectRequestResult.P2PConnection:
					CreateEvent(NetEvent.EType.ConnectionRequest, null, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, new ConnectionRequest(netPeer.ConnectTime, connectionNumber, ConnectionRequestType.PeerToPeer, connRequest.Data, netPeer, this));
					return;
				}
				connectionNumber = (byte)((netPeer.ConnectionNum + 1) % 4);
			}
			netPeer = new NetPeer(this, remoteEndPoint, GetNextPeerId());
			if (TryAddPeer(netPeer) == netPeer)
			{
				CreateEvent(NetEvent.EType.ConnectionRequest, null, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, new ConnectionRequest(connRequest.ConnectionTime, connectionNumber, ConnectionRequestType.Incoming, connRequest.Data, netPeer, this));
			}
		}

		private void DataReceived(byte[] reusableBuffer, int count, IPEndPoint remoteEndPoint)
		{
			NetPacket packet = NetPacketPool.GetPacket(count, false);
			if (!packet.FromBytes(reusableBuffer, 0, count))
			{
				NetPacketPool.Recycle(packet);
				NetDebug.WriteError("[NM] DataReceived: bad!");
				return;
			}
			NetPeer peer;
			bool flag = TryGetPeer(remoteEndPoint, out peer);
			switch (packet.Property)
			{
			case PacketProperty.PeerNotFound:
				if (flag)
				{
					if (peer.ConnectionState == ConnectionState.Connected)
					{
						if (packet.Size == 1)
						{
							NetPacket withProperty = NetPacketPool.GetWithProperty(PacketProperty.PeerNotFound, 9);
							withProperty.RawData[1] = 0;
							FastBitConverter.GetBytes(withProperty.RawData, 2, peer.ConnectTime);
							SendRawAndRecycle(withProperty, remoteEndPoint);
						}
						else if (packet.Size == 10 && packet.RawData[1] == 1 && BitConverter.ToInt64(packet.RawData, 2) == peer.ConnectTime)
						{
							DisconnectPeerForce(peer, DisconnectReason.RemoteConnectionClose, SocketError.Success, null);
						}
					}
				}
				else if (packet.Size == 10 && packet.RawData[1] == 0)
				{
					packet.RawData[1] = 1;
					SendRawAndRecycle(packet, remoteEndPoint);
				}
				break;
			case PacketProperty.DiscoveryRequest:
				if (DiscoveryEnabled)
				{
					CreateEvent(NetEvent.EType.DiscoveryRequest, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, packet);
				}
				break;
			case PacketProperty.DiscoveryResponse:
				CreateEvent(NetEvent.EType.DiscoveryResponse, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, packet);
				break;
			case PacketProperty.UnconnectedMessage:
				if (UnconnectedMessagesEnabled)
				{
					CreateEvent(NetEvent.EType.ReceiveUnconnected, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, packet);
				}
				break;
			case PacketProperty.NatIntroductionRequest:
			case PacketProperty.NatIntroduction:
			case PacketProperty.NatPunchMessage:
				if (NatPunchEnabled)
				{
					NatPunchModule.ProcessMessage(remoteEndPoint, packet);
				}
				break;
			case PacketProperty.InvalidProtocol:
				if (flag && peer.ConnectionState == ConnectionState.Outcoming)
				{
					DisconnectPeerForce(peer, DisconnectReason.InvalidProtocol, SocketError.Success, null);
				}
				break;
			case PacketProperty.Disconnect:
				if (flag)
				{
					DisconnectResult disconnectResult = peer.ProcessDisconnect(packet);
					if (disconnectResult == DisconnectResult.None)
					{
						NetPacketPool.Recycle(packet);
						break;
					}
					DisconnectPeerForce(peer, (disconnectResult == DisconnectResult.Disconnect) ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, SocketError.Success, packet);
				}
				else
				{
					NetPacketPool.Recycle(packet);
				}
				SendRawAndRecycle(NetPacketPool.GetWithProperty(PacketProperty.ShutdownOk, 0), remoteEndPoint);
				break;
			case PacketProperty.ConnectAccept:
			{
				NetConnectAcceptPacket netConnectAcceptPacket = NetConnectAcceptPacket.FromData(packet);
				if (netConnectAcceptPacket != null && flag && peer.ProcessConnectAccept(netConnectAcceptPacket))
				{
					CreateEvent(NetEvent.EType.Connect, peer);
				}
				break;
			}
			case PacketProperty.ConnectRequest:
			{
				if (NetConnectRequestPacket.GetProtocolId(packet) != 7)
				{
					SendRawAndRecycle(NetPacketPool.GetWithProperty(PacketProperty.InvalidProtocol, 0), remoteEndPoint);
					break;
				}
				NetConnectRequestPacket netConnectRequestPacket = NetConnectRequestPacket.FromData(packet);
				if (netConnectRequestPacket != null)
				{
					ProcessConnectRequest(remoteEndPoint, peer, netConnectRequestPacket);
				}
				break;
			}
			default:
				if (flag)
				{
					peer.ProcessPacket(packet);
				}
				else
				{
					SendRawAndRecycle(NetPacketPool.GetWithProperty(PacketProperty.PeerNotFound, 0), remoteEndPoint);
				}
				break;
			}
		}

		internal void ReceiveFromPeer(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			NetPeer peer;
			if (TryGetPeer(remoteEndPoint, out peer))
			{
				DeliveryMethod deliveryMethod;
				switch (packet.Property)
				{
				default:
					deliveryMethod = DeliveryMethod.Unreliable;
					break;
				case PacketProperty.ReliableUnordered:
					deliveryMethod = DeliveryMethod.ReliableUnordered;
					break;
				case PacketProperty.ReliableOrdered:
					deliveryMethod = DeliveryMethod.ReliableOrdered;
					break;
				case PacketProperty.Sequenced:
					deliveryMethod = DeliveryMethod.Sequenced;
					break;
				case PacketProperty.ReliableSequenced:
					deliveryMethod = DeliveryMethod.ReliableSequenced;
					break;
				}
				CreateEvent(NetEvent.EType.Receive, peer, peer.EndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, deliveryMethod, packet);
			}
		}

		public void SendToAll(NetDataWriter writer, DeliveryMethod options)
		{
			SendToAll(writer.Data, 0, writer.Length, options);
		}

		public void SendToAll(byte[] data, DeliveryMethod options)
		{
			SendToAll(data, 0, data.Length, options);
		}

		public void SendToAll(byte[] data, int start, int length, DeliveryMethod options)
		{
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				netPeer.Send(data, start, length, options);
			}
		}

		public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(writer.Data, 0, writer.Length, options, excludePeer);
		}

		public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, 0, data.Length, options, excludePeer);
		}

		public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer)
		{
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if (netPeer != excludePeer)
				{
					netPeer.Send(data, start, length, options);
				}
			}
		}

		public bool Start()
		{
			return Start(0);
		}

		public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port)
		{
			if (IsRunning)
			{
				return false;
			}
			if (!_socket.Bind(addressIPv4, addressIPv6, port, ReuseAddress))
			{
				return false;
			}
			IsRunning = true;
			Thread thread = new Thread(new ThreadStart(UpdateLogic));
			thread.Name = "LogicThread";
			thread.IsBackground = true;
			_logicThread = thread;
			_logicThread.Start();
			return true;
		}

		public bool Start(string addressIPv4, string addressIPv6, int port)
		{
			IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4);
			IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6);
			return Start(addressIPv7, addressIPv8, port);
		}

		public bool Start(int port)
		{
			return Start(IPAddress.Any, IPAddress.IPv6Any, port);
		}

		public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint)
		{
			return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint)
		{
			return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
		{
			if (!IsRunning)
			{
				return false;
			}
			NetPacket withData = NetPacketPool.GetWithData(PacketProperty.UnconnectedMessage, message, start, length);
			return SendRawAndRecycle(withData, remoteEndPoint) > 0;
		}

		public bool SendDiscoveryRequest(NetDataWriter writer, int port)
		{
			return SendDiscoveryRequest(writer.Data, 0, writer.Length, port);
		}

		public bool SendDiscoveryRequest(byte[] data, int port)
		{
			return SendDiscoveryRequest(data, 0, data.Length, port);
		}

		public bool SendDiscoveryRequest(byte[] data, int start, int length, int port)
		{
			if (!IsRunning)
			{
				return false;
			}
			NetPacket withData = NetPacketPool.GetWithData(PacketProperty.DiscoveryRequest, data, start, length);
			bool result = _socket.SendBroadcast(withData.RawData, 0, withData.Size, port);
			NetPacketPool.Recycle(withData);
			return result;
		}

		public bool SendDiscoveryResponse(NetDataWriter writer, IPEndPoint remoteEndPoint)
		{
			return SendDiscoveryResponse(writer.Data, 0, writer.Length, remoteEndPoint);
		}

		public bool SendDiscoveryResponse(byte[] data, IPEndPoint remoteEndPoint)
		{
			return SendDiscoveryResponse(data, 0, data.Length, remoteEndPoint);
		}

		public bool SendDiscoveryResponse(byte[] data, int start, int length, IPEndPoint remoteEndPoint)
		{
			if (!IsRunning)
			{
				return false;
			}
			NetPacket withData = NetPacketPool.GetWithData(PacketProperty.DiscoveryResponse, data, start, length);
			return SendRawAndRecycle(withData, remoteEndPoint) > 0;
		}

		public void Flush()
		{
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				netPeer.Flush();
			}
		}

		public void PollEvents()
		{
			if (UnsyncedEvents)
			{
				return;
			}
			while (true)
			{
				NetEvent evt;
				lock (_netEventsQueue)
				{
					if (_netEventsQueue.Count <= 0)
					{
						break;
					}
					evt = _netEventsQueue.Dequeue();
				}
				ProcessEvent(evt);
			}
		}

		public NetPeer Connect(string address, int port, string key)
		{
			IPEndPoint target = NetUtils.MakeEndPoint(address, port);
			return Connect(target, key);
		}

		public NetPeer Connect(string address, int port, NetDataWriter connectionData)
		{
			IPEndPoint target = NetUtils.MakeEndPoint(address, port);
			return Connect(target, connectionData);
		}

		public NetPeer Connect(IPEndPoint target, string key)
		{
			return Connect(target, NetDataWriter.FromString(key));
		}

		public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData)
		{
			if (!IsRunning)
			{
				throw new InvalidOperationException("Client is not running");
			}
			byte connectNum = 0;
			NetPeer peer;
			if (TryGetPeer(target, out peer))
			{
				ConnectionState connectionState = peer.ConnectionState;
				if (connectionState == ConnectionState.Incoming || connectionState == ConnectionState.Outcoming || connectionState == ConnectionState.Connected)
				{
					return peer;
				}
				connectNum = (byte)((peer.ConnectionNum + 1) % 4);
				RemovePeer(peer);
			}
			return TryAddPeer(new NetPeer(this, target, GetNextPeerId(), connectNum, connectionData));
		}

		public void Stop()
		{
			Stop(true);
		}

		public void Stop(bool sendDisconnectMessages)
		{
			if (!IsRunning)
			{
				return;
			}
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages);
			}
			IsRunning = false;
			_logicThread.Join();
			_logicThread = null;
			_socket.Close();
			_peersLock.EnterWriteLock();
			_headPeer = null;
			_peersDict.Clear();
			_peersLock.ExitWriteLock();
			_connectedPeersCount = 0;
			lock (_netEventsQueue)
			{
				_netEventsQueue.Clear();
			}
		}

		public int GetPeersCount(ConnectionState peerState)
		{
			int num = 0;
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if ((netPeer.ConnectionState & peerState) != 0)
				{
					num++;
				}
			}
			return num;
		}

		[Obsolete("Use GetPeers(ConnectionState peerState)")]
		public NetPeer[] GetPeers()
		{
			return GetPeers(ConnectionState.Outcoming | ConnectionState.Connected);
		}

		public NetPeer[] GetPeers(ConnectionState peerState)
		{
			List<NetPeer> list = new List<NetPeer>();
			GetPeersNonAlloc(list, peerState);
			return list.ToArray();
		}

		public void GetPeersNonAlloc(List<NetPeer> peers, ConnectionState peerState)
		{
			peers.Clear();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if ((netPeer.ConnectionState & peerState) != 0)
				{
					peers.Add(netPeer);
				}
			}
		}

		public void DisconnectAll()
		{
			DisconnectAll(null, 0, 0);
		}

		public void DisconnectAll(byte[] data, int start, int count)
		{
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				DisconnectPeer(netPeer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, false, data, start, count, null);
			}
		}

		public void DisconnectPeerForce(NetPeer peer)
		{
			DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, null);
		}

		public void DisconnectPeer(NetPeer peer)
		{
			DisconnectPeer(peer, null, 0, 0);
		}

		public void DisconnectPeer(NetPeer peer, byte[] data)
		{
			DisconnectPeer(peer, data, 0, data.Length);
		}

		public void DisconnectPeer(NetPeer peer, NetDataWriter writer)
		{
			DisconnectPeer(peer, writer.Data, 0, writer.Length);
		}

		public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count)
		{
			DisconnectPeer(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, false, data, start, count, null);
		}

		public IEnumerator<NetPeer> GetEnumerator()
		{
			_003CGetEnumerator_003Ed__104 _003CGetEnumerator_003Ed__ = new _003CGetEnumerator_003Ed__104(0);
			_003CGetEnumerator_003Ed__._003C_003E4__this = this;
			return _003CGetEnumerator_003Ed__;
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}
